/*
 * rewrite/rowsecurity.c
 *	  支持行级安全性（即 RLS）策略的例程。
 *
 * PostgreSQL 中的策略提供了一种机制来限制用户返回哪些记录，以及用户被允许将哪些记录添加到表中。
 *
 * 可以为特定角色、特定命令或通过扩展提供定义策略。 也可以在没有显式定义任何策略的情况下
 * 为表启用行安全性，在这种情况下，应用默认拒绝策略。
 *
 * 系统的任何部分，返回记录给用户，或接受用户记录以添加到表中，都需要考虑与表相关的策略（如果有）。 
 * 对于普通查询，这通过在重写期间为查询中的每个 RTE 调用 get_row_security_policies() 来处理。
 * 这将返回表的策略定义的表达式列表，该列表将插入到 RTE 的 securityQuals 列表中。
 * 对于修改表的查询，表的策略的任何 WITH CHECK 子句也将被返回并插入到查询的 WithCheckOptions 列表中，
 * 以检查被添加到表中的每一行。
 * 系统的其他部分（例如：COPY）只是构建一个普通查询并使用它，如果要应用 RLS。
 *
 * 检查是否应启用 RLS 通过 check_enable_rls() 提供，该函数返回一个枚举（在 rowsecurity.h 中定义），
 * 以指示是否应启用 RLS（RLS_ENABLED），或绕过（RLS_NONE 或 RLS_NONE_ENV）。 
 * RLS_NONE_ENV 表示在当前环境中应绕过 RLS，但如果 row_security GUC 或当前角色发生变化，情况可能会改变。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/pg_class.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/pg_list.h"
#include "nodes/plannodes.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rowsecurity.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/rls.h"
#include "utils/syscache.h"


#ifdef FDD
//用来接口传参数，个数太多
typedef struct _FdbMacParam
{
	Query *root;
	RangeTblEntry *rte;
	int rt_index;
	List **securityQuals;
	List **withCheckOptions;
	bool *hasRowSecurity;
	bool *hasSubLinks;
	Relation rel;
}FdbMacParam;
/*
添加强制访问策略，如果mac功能关闭或者对应的表没有配置策略，不做任何事
主要逻辑根据get_row_security_policies修改
*/
static void fdb_get_mac_polices(Query *fc_root, RangeTblEntry *fc_rte, int fc_rt_index,
						  List **fc_securityQuals, List **fc_withCheckOptions,
						  bool *fc_hasRowSecurity, bool *fc_hasSubLinks);

static void fdb_get_mac_polices_for_merge(Query *fc_root, RangeTblEntry *fc_rte, int fc_rt_index,
						List **fc_securityQuals, List **fc_withCheckOptions,
						bool *fc_hasRowSecurity, bool *fc_hasSubLinks);


static void fdb_get_mac_polices_internal(FdbMacParam *fc_param, CmdType fc_commandType, List  *fc_relPolicyCulomn);


fdb_mac_restrictive_hook_type fdb_rls_mac_restrictive_hook = NULL;
fdb_mac_permissive_hook_type  fdb_mac_permissive_hook = NULL;
fdb_rel_need_mac_check_hook_type fdb_rel_need_mac_check_hook = NULL;
#endif
static void get_policies_for_relation(Relation fc_relation,
									  CmdType fc_cmd, Oid fc_user_id,
									  List **fc_permissive_policies,
									  List **fc_restrictive_policies);

static void sort_policies_by_name(List *fc_policies);

static int	fc_row_security_policy_cmp(const ListCell *fc_a, const ListCell *fc_b);

static void add_security_quals(int fc_rt_index,
							   List *fc_permissive_policies,
							   List *fc_restrictive_policies,
							   List **fc_securityQuals,
							   bool *fc_hasSubLinks);

static void add_with_check_options(Relation fc_rel,
								   int fc_rt_index,
								   WCOKind fc_kind,
								   List *fc_permissive_policies,
								   List *fc_restrictive_policies,
								   List **fc_withCheckOptions,
								   bool *fc_hasSubLinks,
								   bool fc_force_using);

static bool fc_check_role_for_policy(ArrayType *fc_policy_roles, Oid fc_user_id);

/*
 * 允许扩展添加其自身安全策略的钩子
 *
 * row_security_policy_hook_permissive 可用于添加与其他
 * 宽松政策结合的政策，使用 OR。
 *
 * row_security_policy_hook_restrictive 可用于添加强制实施的政策，
 * 无论其他政策如何（它们使用 AND 组合）。
 */
row_security_policy_hook_type row_security_policy_hook_permissive = NULL;
row_security_policy_hook_type row_security_policy_hook_restrictive = NULL;


#ifdef FDD
bool fdb_need_mac(Oid fc_relid, List **fc_poliyColumns)
{
	List *fc_tmpPolyColums = NULL;
	bool fc_needMac = false;
	if (fdb_rls_mac_restrictive_hook == NULL || fdb_mac_permissive_hook == NULL || fdb_rel_need_mac_check_hook == NULL)
	{
		//fdb_mac插件被卸载
		return fc_needMac;
	}

	if (fc_poliyColumns == NULL)
	{
		fc_needMac = fdb_rel_need_mac_check_hook(fc_relid, &fc_tmpPolyColums);
	}
	else
	{
		fc_needMac = fdb_rel_need_mac_check_hook(fc_relid, fc_poliyColumns);
	}

	return fc_needMac;
}

static void fdb_get_mac_polices_internal(FdbMacParam *fc_param, CmdType fc_commandType, List  *fc_relPolicyCulomn)
{
	List	   *fc_permissive_policies = NULL;
	List	   *fc_restrictive_policies = NULL;
	
	fc_restrictive_policies = fdb_rls_mac_restrictive_hook(fc_commandType ,fc_param->rel, fc_param->rte, fc_relPolicyCulomn);

	if (fc_restrictive_policies != NULL)
	{
		fc_permissive_policies = fdb_mac_permissive_hook(fc_param->rel);
	}

	if (fc_restrictive_policies != NULL && fc_permissive_policies != NULL)
	{
		if (fc_commandType == CMD_SELECT ||
		fc_commandType == CMD_UPDATE ||
		fc_commandType == CMD_DELETE)
		{
			
			add_security_quals(fc_param->rt_index,
						   fc_permissive_policies,
						   fc_restrictive_policies,
						   fc_param->securityQuals,
						   fc_param->hasSubLinks);
			*(fc_param->hasRowSecurity) = true;
		}		
	}

	//update或者delete不用做select限制了
	/*
	 * 对于 INSERT 和 UPDATE，添加 withCheckOptions 以验证任何新增
	 * 记录与安全策略的一致性。 这将使用
	 * 每个政策的 WITH CHECK 子句，或者如果未定义显式
	 * WITH CHECK 子句，则使用其 USING 子句。
	 */
	if (fc_commandType == CMD_INSERT || fc_commandType == CMD_UPDATE)
	{
		/* 这应该是目标关系 */
		Assert(fc_param->rt_index == fc_param->root->resultRelation);

		//permissive不为空才走
		if (fc_permissive_policies != NULL)
		{
			add_with_check_options(fc_param->rel, fc_param->rt_index,
							   fc_commandType == CMD_INSERT ?
							   WCO_RLS_INSERT_CHECK : WCO_RLS_UPDATE_CHECK,
							   fc_permissive_policies,
							   fc_restrictive_policies,
							   fc_param->withCheckOptions,
							   fc_param->hasSubLinks,
							   false);
			*(fc_param->hasRowSecurity) = true;
		}
		

		//insert returning 不做select限制，update不做select限制
		/*
		 * 对于 INSERT ... ON CONFLICT DO UPDATE，我们需要额外的政策
		 * 检查，以便对可能适用相同 RTE 的 UPDATE 进行检查。
		 */
		if (fc_commandType == CMD_INSERT &&
			fc_param->root->onConflict && fc_param->root->onConflict->action == ONCONFLICT_UPDATE)
		{
			List	   *fc_conflict_permissive_policies = NIL;
			List	   *fc_conflict_restrictive_policies = NIL;
			List	   *fc_conflict_select_permissive_policies = NIL;
			List	   *fc_conflict_select_restrictive_policies = NIL;



			fc_conflict_restrictive_policies = fdb_rls_mac_restrictive_hook(CMD_UPDATE , fc_param->rel, fc_param->rte,fc_relPolicyCulomn);

			//这里不用检查用户了，所有用户都限制
			if (fc_conflict_restrictive_policies != NULL)
			{
				fc_conflict_permissive_policies = fdb_mac_permissive_hook(fc_param->rel);
				add_with_check_options(fc_param->rel, fc_param->rt_index,
								   WCO_RLS_CONFLICT_CHECK,
								   fc_conflict_permissive_policies,
								   fc_conflict_restrictive_policies,
								   fc_param->withCheckOptions,
								   fc_param->hasSubLinks,
								   true);
				*(fc_param->hasRowSecurity) = true;
			}

			//ON CONFLICT DO UPDATE不做select限制
		
			/* 强制执行 UPDATE 政策的 WITH CHECK 子句 */
			if (fc_conflict_permissive_policies != NULL)
			{
				
				add_with_check_options(fc_param->rel, fc_param->rt_index,
									   WCO_RLS_UPDATE_CHECK,
									   fc_conflict_permissive_policies,
									   fc_conflict_restrictive_policies,
									   fc_param->withCheckOptions,
									   fc_param->hasSubLinks,
									   false);
				*(fc_param->hasRowSecurity) = true;
			}
			
	
			/*
			 * 将 ALL/SELECT 政策作为 WCO_RLS_UPDATE_CHECK WCOs 添加，以确保
			 * 在执行 INSERT .. ON CONFLICT DO UPDATE 的 UPDATE
			 * 路径时，最终更新的行是可见的，如果此关系需要
			 * SELECT 权限。
			 */
			if (fc_param->rte->requiredPerms & ACL_SELECT && fc_conflict_select_permissive_policies != NULL)
			{
				add_with_check_options(fc_param->rel, fc_param->rt_index,
									   WCO_RLS_UPDATE_CHECK,
									   fc_conflict_select_permissive_policies,
									   fc_conflict_select_restrictive_policies,
									   fc_param->withCheckOptions,
									   fc_param->hasSubLinks,
									   true);
				*(fc_param->hasRowSecurity) = true;
			}
		}
	}
}

static void fdb_get_mac_polices_for_merge(Query *fc_root, RangeTblEntry *fc_rte, int fc_rt_index,
						List **fc_securityQuals, List **fc_withCheckOptions,
						bool *fc_hasRowSecurity, bool *fc_hasSubLinks)
{
	Relation	fc_rel = NULL;
	List	   *fc_relPolicyCulomn = NULL;
	ListCell   *fc_actionCell = NULL;
	FdbMacParam fc_macParam;

	if (!fdb_need_mac(fc_rte->relid, &fc_relPolicyCulomn))
	{
		return;
	}

	/* 如果这不是正常关系，则立即返回 */
	if (fc_rte->relkind != RELKIND_RELATION &&
		fc_rte->relkind != RELKIND_PARTITIONED_TABLE)
		return;

	//如果是merge，根据actionlist处理mac
	//merge操作，对目标表只有insert，update，delete
	//只有update需要判断是否是更新策略列，insert和delete都是写操作
	fc_rel = table_open(fc_rte->relid, NoLock);

	fc_macParam.hasRowSecurity = fc_hasRowSecurity;
	fc_macParam.hasSubLinks = fc_hasSubLinks;
	fc_macParam.rel = fc_rel;
	fc_macParam.root = fc_root;
	fc_macParam.rte = fc_rte;
	fc_macParam.rt_index = fc_rt_index;
	fc_macParam.securityQuals = fc_securityQuals;
	fc_macParam.withCheckOptions = fc_withCheckOptions;
	
	foreach(fc_actionCell, fc_root->mergeActionList)
	{
		MergeAction *fc_action = (MergeAction *) lfirst(fc_actionCell);
		fdb_get_mac_polices_internal(&fc_macParam, fc_action->commandType, fc_relPolicyCulomn);
	}

	table_close(fc_rel, NoLock);

	if (*fc_hasRowSecurity)
	{
		setRuleCheckAsUser((Node *) *fc_securityQuals, fc_rte->checkAsUser);
		setRuleCheckAsUser((Node *) *fc_withCheckOptions, fc_rte->checkAsUser);
	}
}


/*
添加强制访问策略，如果mac功能关闭或者对应的表没有配置策略，不做任何事
主要逻辑根据get_row_security_policies修改
*/
static void fdb_get_mac_polices(Query *fc_root, RangeTblEntry *fc_rte, int fc_rt_index,
						  List **fc_securityQuals, List **fc_withCheckOptions,
						  bool *fc_hasRowSecurity, bool *fc_hasSubLinks)
{
	Relation	fc_rel = NULL;
	CmdType		fc_commandType;
	List       *fc_relPolicyCulomn = NULL;
	FdbMacParam fc_macParam;

	if (!fdb_need_mac(fc_rte->relid, &fc_relPolicyCulomn))
	{
		return;
	}

	/* 返回值的默认值 */
	*fc_securityQuals = NIL;
	*fc_withCheckOptions = NIL;
	*fc_hasRowSecurity = false;
	*fc_hasSubLinks = false;

	/* 如果这不是正常关系，则立即返回 */
	if (fc_rte->relkind != RELKIND_RELATION &&
		fc_rte->relkind != RELKIND_PARTITIONED_TABLE)
		return;

	//不用检查用户了
	//不继续检查行安全策略了
	fc_rel = table_open(fc_rte->relid, NoLock);

	fc_commandType = fc_rt_index == fc_root->resultRelation ?
		fc_root->commandType : CMD_SELECT;

	//select fro update/share不做update限制
	fc_macParam.hasRowSecurity = fc_hasRowSecurity;
	fc_macParam.hasSubLinks = fc_hasSubLinks;
	fc_macParam.rel = fc_rel;
	fc_macParam.root = fc_root;
	fc_macParam.rte = fc_rte;
	fc_macParam.rt_index = fc_rt_index;
	fc_macParam.securityQuals = fc_securityQuals;
	fc_macParam.withCheckOptions = fc_withCheckOptions;

	fdb_get_mac_polices_internal(&fc_macParam, fc_commandType, fc_relPolicyCulomn);
	table_close(fc_rel, NoLock);
	
	if (*fc_hasRowSecurity)
	{
		setRuleCheckAsUser((Node *) *fc_securityQuals, fc_rte->checkAsUser);
		setRuleCheckAsUser((Node *) *fc_withCheckOptions, fc_rte->checkAsUser);
	}
}
#endif
/*
 * 获取应该应用于指定 RTE 的行安全限定符和 WithCheckOption 检查。
 *
 * 此外，如果启用了行级安全，hasRowSecurity 设置为 true
 * （即使此 RTE 没有任何行安全限定符），如果返回的任何限定符
 * 包含子链接，则 hasSubLinks 设置为 true。
 */
void get_row_security_policies(Query *fc_root, RangeTblEntry *fc_rte, int fc_rt_index,
						  List **fc_securityQuals, List **fc_withCheckOptions,
						  bool *fc_hasRowSecurity, bool *fc_hasSubLinks)
{
	Oid			fc_user_id;
	int			fc_rls_status;
	Relation	fc_rel;
	CmdType		fc_commandType;
	List	   *fc_permissive_policies;
	List	   *fc_restrictive_policies;

	/* 返回值的默认值 */
	*fc_securityQuals = NIL;
	*fc_withCheckOptions = NIL;
	*fc_hasRowSecurity = false;
	*fc_hasSubLinks = false;

	/* 如果这不是正常关系，则立即返回 */
	if (fc_rte->relkind != RELKIND_RELATION &&
		fc_rte->relkind != RELKIND_PARTITIONED_TABLE)
		return;

	/* 如果设置了 checkAsUser，请切换到 checkAsUser */
	fc_user_id = fc_rte->checkAsUser ? fc_rte->checkAsUser : GetUserId();

	/* 确定此 RLS 的状态，显式传递 checkAsUser */
	fc_rls_status = check_enable_rls(fc_rte->relid, fc_rte->checkAsUser, false);

#ifdef FDD	
	fc_commandType = fc_rt_index == fc_root->resultRelation ?
		fc_root->commandType : CMD_SELECT;


	//先尝试获取mac策略，如果不用mac，没有做任何事
	//mac需要绕开rls开关
	if (fc_commandType == CMD_MERGE)
	{
		fdb_get_mac_polices_for_merge(fc_root, fc_rte, fc_rt_index, fc_securityQuals, fc_withCheckOptions
		, fc_hasRowSecurity, fc_hasSubLinks);
	}
	else
	{
		fdb_get_mac_polices(fc_root, fc_rte, fc_rt_index, fc_securityQuals, fc_withCheckOptions
		, fc_hasRowSecurity, fc_hasSubLinks);
	}
#endif
	/* 如果此表上根本没有 RLS，则无需执行任何操作 */
	if (fc_rls_status == RLS_NONE)
		return;

	/*
	 * RLS_NONE_ENV 意味着我们现在不进行任何 RLS，但这可能会
	 * 随着环境的变化而变化，因此我们将其标记为 hasRowSecurity
	 * 以强制在环境变化时重新规划。
	 */
	if (fc_rls_status == RLS_NONE_ENV)
	{
		/*
		 * 表示此查询可能涉及 RLS，因此如果环境发生变化（GUCs，
		 * 角色），必须重新规划，但我们在此不添加任何内容。
		 */
		*fc_hasRowSecurity = true;

		return;
	}

	/*
	 * 此关系启用了 RLS。
	 *
	 * 根据命令类型获取应该应用的安全策略。请注意，如果这
	 * 不是目标关系，我们实际上希望获取关系的 SELECT
	 * 政策，而不论查询命令类型，例如在 UPDATE t1 ... FROM t2
	 * 中，我们需要应用 t1 的 UPDATE 政策和 t2 的 SELECT
	 * 政策。
	 */
	fc_rel = table_open(fc_rte->relid, NoLock);
#ifndef FDD
	fc_commandType = fc_rt_index == fc_root->resultRelation ?
		fc_root->commandType : CMD_SELECT;
#endif
	/*
	 * 在某些情况下，我们需要应用与多种命令类型关联的 USING
	 * 政策（控制记录的可见性）（见下面的特定情况）。
	 *
	 * 在考虑应用这些 USING 政策的顺序时，我们首先倾向于应用
	 * 权限更高的政策，即允许用户锁定记录的政策（UPDATE 和
	 * DELETE），然后是那些不允许的政策（SELECT）。
	 *
	 * 请注意，优化器可以自由地向下推送和重新排序使用
	 * leakproof 函数的限定符。
	 *
	 * 在所有情况下，如果没有政策子句允许特定类型操作
	 * 访问表中的行，则将添加一个始终为假的子句（默认拒绝政策）
	 * （请参见 add_security_quals）。
	 */

	
/*
	 * 对于 SELECT，如果需要 UPDATE 权限（例如：用户指定了 FOR [KEY] UPDATE/SHARE），
	 * 那么首先添加 UPDATE USING 资格。
	 *
	 * 这样，我们可以通过 UPDATE USING 策略筛选出用户没有访问权限的
	 * SELECT FOR SHARE/UPDATE 记录，类似于我们对这些查询要求正常的 UPDATE 权限。
	 */
	if (fc_commandType == CMD_SELECT && fc_rte->requiredPerms & ACL_UPDATE)
	{
		List	   *update_permissive_policies;
		List	   *update_restrictive_policies;

		get_policies_for_relation(fc_rel, CMD_UPDATE, fc_user_id,
								  &update_permissive_policies,
								  &update_restrictive_policies);

		add_security_quals(fc_rt_index,
						   update_permissive_policies,
						   update_restrictive_policies,
						   fc_securityQuals,
						   fc_hasSubLinks);
	}

	/*
	 * 对于 SELECT、UPDATE 和 DELETE，添加安全资格来执行 USING 策略。
	 * 这些安全资格控制对现有表行的访问。
	 * 限制性策略使用 AND 连接在一起，而宽松策略则使用 OR 连接在一起。
	 */

	get_policies_for_relation(fc_rel, fc_commandType, fc_user_id, &fc_permissive_policies,
							  &fc_restrictive_policies);

	if (fc_commandType == CMD_SELECT ||
		fc_commandType == CMD_UPDATE ||
		fc_commandType == CMD_DELETE)
		add_security_quals(fc_rt_index,
						   fc_permissive_policies,
						   fc_restrictive_policies,
						   fc_securityQuals,
						   fc_hasSubLinks);

	/*
	 * 类似于上述，在 UPDATE、DELETE 或 MERGE 期间，如果还需要 SELECT 权限
	 * （例如：当存在 RETURNING 子句，或者用户提供了一个涉及关系中列的 WHERE 子句时），我们
	 * 首先收集 CMD_SELECT 策略并通过 add_security_quals 添加它们。
	 *
	 * 通过这种方式，我们过滤掉任何通过 ALL 或 SELECT USING 策略不可见的记录。
	 */
	if ((fc_commandType == CMD_UPDATE || fc_commandType == CMD_DELETE ||
		 fc_commandType == CMD_MERGE) &&
		fc_rte->requiredPerms & ACL_SELECT)
	{
		List	   *fc_select_permissive_policies;
		List	   *fc_select_restrictive_policies;

		get_policies_for_relation(fc_rel, CMD_SELECT, fc_user_id,
								  &fc_select_permissive_policies,
								  &fc_select_restrictive_policies);

		add_security_quals(fc_rt_index,
						   fc_select_permissive_policies,
						   fc_select_restrictive_policies,
						   fc_securityQuals,
						   fc_hasSubLinks);
	}

	/*
	 * 对于 INSERT 和 UPDATE，添加 withCheckOptions 以验证任何新增
	 * 记录与安全策略的一致性。 这将使用
	 * 每个政策的 WITH CHECK 子句，或者如果未定义显式
	 * WITH CHECK 子句，则使用其 USING 子句。
	 */
	if (fc_commandType == CMD_INSERT || fc_commandType == CMD_UPDATE)
	{
		/* 这应该是目标关系 */
		Assert(fc_rt_index == fc_root->resultRelation);

		add_with_check_options(fc_rel, fc_rt_index,
							   fc_commandType == CMD_INSERT ?
							   WCO_RLS_INSERT_CHECK : WCO_RLS_UPDATE_CHECK,
							   fc_permissive_policies,
							   fc_restrictive_policies,
							   fc_withCheckOptions,
							   fc_hasSubLinks,
							   false);

		/*
		 * 获取并添加 ALL/SELECT 策略，如果此关系需要 SELECT 权限
		 * （例如：当使用 RETURNING 时）。这些作为 WCO 策略而不是安全
		 * 资格被添加，以确保如果违反了某个策略，则会引发错误；否则，我们可能会
		 * 默默地删除要添加的行。
		 */
		if (fc_rte->requiredPerms & ACL_SELECT)
		{
			List	   *fc_select_permissive_policies = NIL;
			List	   *fc_select_restrictive_policies = NIL;

			get_policies_for_relation(fc_rel, CMD_SELECT, fc_user_id,
									  &fc_select_permissive_policies,
									  &fc_select_restrictive_policies);
			add_with_check_options(fc_rel, fc_rt_index,
								   fc_commandType == CMD_INSERT ?
								   WCO_RLS_INSERT_CHECK : WCO_RLS_UPDATE_CHECK,
								   fc_select_permissive_policies,
								   fc_select_restrictive_policies,
								   fc_withCheckOptions,
								   fc_hasSubLinks,
								   true);
		}

		/*
		 * 对于 INSERT ... ON CONFLICT DO UPDATE，我们需要额外的政策
		 * 检查，以便对可能适用相同 RTE 的 UPDATE 进行检查。
		 */
		if (fc_commandType == CMD_INSERT &&
			fc_root->onConflict && fc_root->onConflict->action == ONCONFLICT_UPDATE)
		{
			List	   *fc_conflict_permissive_policies;
			List	   *fc_conflict_restrictive_policies;
			List	   *fc_conflict_select_permissive_policies = NIL;
			List	   *fc_conflict_select_restrictive_policies = NIL;

			/* 获取适用于辅助 UPDATE 的策略 */
			get_policies_for_relation(fc_rel, CMD_UPDATE, fc_user_id,
									  &fc_conflict_permissive_policies,
									  &fc_conflict_restrictive_policies);

			/*
			 * 使用 WCO 而不是安全资格强制执行 UPDATE 策略的 USING 子句。
			 * 这确保如果由于 RLS 而无法更新冲突的行，则会引发错误，而不是
			 * 变化被默默丢弃。
			 */
			add_with_check_options(fc_rel, fc_rt_index,
								   WCO_RLS_CONFLICT_CHECK,
								   fc_conflict_permissive_policies,
								   fc_conflict_restrictive_policies,
								   fc_withCheckOptions,
								   fc_hasSubLinks,
								   true);

			/*
			 * 获取并添加 ALL/SELECT 策略，作为 WCO_RLS_CONFLICT_CHECK WCOs
			 * 确保在采取 INSERT .. ON CONFLICT DO UPDATE 的 UPDATE 路径时
			 * 考虑它们，如果此关系需要 SELECT 权限，同样作为 WCO 策略，以避免
			 * 默默地丢弃数据。见上文。
			 */
			if (fc_rte->requiredPerms & ACL_SELECT)
			{
				get_policies_for_relation(fc_rel, CMD_SELECT, fc_user_id,
										  &fc_conflict_select_permissive_policies,
										  &fc_conflict_select_restrictive_policies);
				add_with_check_options(fc_rel, fc_rt_index,
									   WCO_RLS_CONFLICT_CHECK,
									   fc_conflict_select_permissive_policies,
									   fc_conflict_select_restrictive_policies,
									   fc_withCheckOptions,
									   fc_hasSubLinks,
									   true);
			}

			/* 强制执行 UPDATE 政策的 WITH CHECK 子句 */
			add_with_check_options(fc_rel, fc_rt_index,
								   WCO_RLS_UPDATE_CHECK,
								   fc_conflict_permissive_policies,
								   fc_conflict_restrictive_policies,
								   fc_withCheckOptions,
								   fc_hasSubLinks,
								   false);

			/*
			 * 将 ALL/SELECT 政策作为 WCO_RLS_UPDATE_CHECK WCOs 添加，以确保
			 * 在执行 INSERT .. ON CONFLICT DO UPDATE 的 UPDATE
			 * 路径时，最终更新的行是可见的，如果此关系需要
			 * SELECT 权限。
			 */
			if (fc_rte->requiredPerms & ACL_SELECT)
				add_with_check_options(fc_rel, fc_rt_index,
									   WCO_RLS_UPDATE_CHECK,
									   fc_conflict_select_permissive_policies,
									   fc_conflict_select_restrictive_policies,
									   fc_withCheckOptions,
									   fc_hasSubLinks,
									   true);
		}
	}

	/*
	 * 对于 MERGE，我们获取 UPDATE、DELETE 和 INSERT（以及 ALL）的策略，
	 * 并设置它们，以便我们可以根据采取的最终行动来强制实施适当的策略。
	 *
	 * 我们已经在上面获取了 SELECT 策略，以检查现有行，
	 * 但我们还必须检查由 UPDATE 操作创建的新行是否可见，如果
	 * 此关系需要 SELECT 权限。我们不这样做是因为 INSERT 操作，
	 * 因为仅在具有 RETURNING 列表时 INSERT 命令才会进行此检查，而
	 * MERGE 不支持 RETURNING。
	 *
	 * 我们不将 UPDATE/DELETE USING 资格推送到 RTE，因为我们并不
	 * 真正想在扫描关系时应用它们，因为我们不知道最终会进行
	 * UPDATE 还是 DELETE。我们在决定目标元组的最终操作后应用相关的策略。
	 *
	 * XXX 我们将 USING 资格设置为 WITH CHECK。如果 RLS 禁止对
	 * 目标行进行 UPDATE/DELETE，我们将抛出错误，而不是默默忽略该行。
	 * 这与普通的 UPDATE/DELETE 工作方式不同，更符合 INSERT ON CONFLICT DO UPDATE
	 * 的处理方式。
	 */
	if (fc_commandType == CMD_MERGE)
	{
		List	   *fc_merge_update_permissive_policies;
		List	   *fc_merge_update_restrictive_policies;
		List	   *fc_merge_delete_permissive_policies;
		List	   *fc_merge_delete_restrictive_policies;
		List	   *fc_merge_insert_permissive_policies;
		List	   *fc_merge_insert_restrictive_policies;

		/*
		 * 获取 UPDATE 策略并设置它们，以便在进行 UPDATE 之前
		 * 在现有目标行上执行。
		 */
		get_policies_for_relation(fc_rel, CMD_UPDATE, fc_user_id,
								  &fc_merge_update_permissive_policies,
								  &fc_merge_update_restrictive_policies);

		/*
		 * WCO_RLS_MERGE_UPDATE_CHECK 用于检查现有目标行上的
		 * UPDATE USING 资格。
		 */
		add_with_check_options(fc_rel, fc_rt_index,
							   WCO_RLS_MERGE_UPDATE_CHECK,
							   fc_merge_update_permissive_policies,
							   fc_merge_update_restrictive_policies,
							   fc_withCheckOptions,
							   fc_hasSubLinks,
							   true);

		/* 强制执行 UPDATE 政策的 WITH CHECK 子句 */
		add_with_check_options(fc_rel, fc_rt_index,
							   WCO_RLS_UPDATE_CHECK,
							   fc_merge_update_permissive_policies,
							   fc_merge_update_restrictive_policies,
							   fc_withCheckOptions,
							   fc_hasSubLinks,
							   false);

		/*
		 * 作为 WCO_RLS_UPDATE_CHECK WCOs 添加 ALL/SELECT 策略，以确保
		 * 在执行 UPDATE 操作时更新的行是可见的，前提是此关系需要 SELECT 权限。
		 */
		if (fc_rte->requiredPerms & ACL_SELECT)
		{
			List	   *fc_merge_select_permissive_policies;
			List	   *fc_merge_select_restrictive_policies;

			get_policies_for_relation(fc_rel, CMD_SELECT, fc_user_id,
									  &fc_merge_select_permissive_policies,
									  &fc_merge_select_restrictive_policies);
			add_with_check_options(fc_rel, fc_rt_index,
								   WCO_RLS_UPDATE_CHECK,
								   fc_merge_select_permissive_policies,
								   fc_merge_select_restrictive_policies,
								   fc_withCheckOptions,
								   fc_hasSubLinks,
								   true);
		}

		/*
		 * 获取 DELETE 策略并设置它们，以便在进行 DELETE 之前
		 * 在现有目标行上执行。
		 */
		get_policies_for_relation(fc_rel, CMD_DELETE, fc_user_id,
								  &fc_merge_delete_permissive_policies,
								  &fc_merge_delete_restrictive_policies);

		/*
		 * WCO_RLS_MERGE_DELETE_CHECK 用于检查现有目标行上的
		 * DELETE USING 资格。
		 */
		add_with_check_options(fc_rel, fc_rt_index,
							   WCO_RLS_MERGE_DELETE_CHECK,
							   fc_merge_delete_permissive_policies,
							   fc_merge_delete_restrictive_policies,
							   fc_withCheckOptions,
							   fc_hasSubLinks,
							   true);

		/*
		 * 对于 INSERT 策略不需要特殊处理。它们将在 ExecInsert() 中
		 * 被检查和执行。但我们必须将它们添加到 withCheckOptions。
		 */
		get_policies_for_relation(fc_rel, CMD_INSERT, fc_user_id,
								  &fc_merge_insert_permissive_policies,
								  &fc_merge_insert_restrictive_policies);

		add_with_check_options(fc_rel, fc_rt_index,
							   WCO_RLS_INSERT_CHECK,
							   fc_merge_insert_permissive_policies,
							   fc_merge_insert_restrictive_policies,
							   fc_withCheckOptions,
							   fc_hasSubLinks,
							   false);
	}

	table_close(fc_rel, NoLock);

	/*
	 * 将 checkAsUser 复制到行安全资格和 WithCheckOption 检查中，
	 * 以防它们包含任何引用其他关系的子查询。
	 */
	setRuleCheckAsUser((Node *) *fc_securityQuals, fc_rte->checkAsUser);
	setRuleCheckAsUser((Node *) *fc_withCheckOptions, fc_rte->checkAsUser);

	/*
	 * 将此查询标记为具有行安全性，以便 plancache 可以在必要时使其失效
	 * （例如：角色更改）
	 */
	*fc_hasRowSecurity = true;
}

/*
 * get_policies_for_relation
 *
 * 根据命令类型和角色返回要应用于指定关系的宽松和严格政策列表。
 *
 * 这包括扩展添加的任何策略。
 */
static void get_policies_for_relation(Relation fc_relation, CmdType fc_cmd, Oid fc_user_id,
						  List **fc_permissive_policies,
						  List **fc_restrictive_policies)
{
	ListCell   *fc_item;

	*fc_permissive_policies = NIL;
	*fc_restrictive_policies = NIL;

	/* 首先查找与关系相关的所有内部策略。 */
	foreach(fc_item, fc_relation->rd_rsdesc->policies)
	{
		bool		fc_cmd_matches = false;
		RowSecurityPolicy *fc_policy = (RowSecurityPolicy *) lfirst(fc_item);

		/* 如果存在，始终添加 ALL 策略。 */
		if (fc_policy->polcmd == '*')
			fc_cmd_matches = true;
		else
		{
			/* 检查策略是否适用于指定的命令类型 */
			switch (fc_cmd)
			{
				case CMD_SELECT:
					if (fc_policy->polcmd == ACL_SELECT_CHR)
						fc_cmd_matches = true;
					break;
				case CMD_INSERT:
					if (fc_policy->polcmd == ACL_INSERT_CHR)
						fc_cmd_matches = true;
					break;
				case CMD_UPDATE:
					if (fc_policy->polcmd == ACL_UPDATE_CHR)
						fc_cmd_matches = true;
					break;
				case CMD_DELETE:
					if (fc_policy->polcmd == ACL_DELETE_CHR)
						fc_cmd_matches = true;
					break;
				case CMD_MERGE:

					/*
					 * 我们不支持针对 MERGE 命令的单独策略。
					 * 而是根据其他命令定义的策略进行推导。
					 */
					break;
				default:
					elog(ERROR, "unrecognized policy command type %d",
						 (int) fc_cmd);
					break;
			}
		}

		/*
		 * 如果该策略适用于指定角色，则将其添加到相关的政策列表中。
		 */
		if (fc_cmd_matches && fc_check_role_for_policy(fc_policy->roles, fc_user_id))
		{
			if (fc_policy->permissive)
				*fc_permissive_policies = lappend(*fc_permissive_policies, fc_policy);
			else
				*fc_restrictive_policies = lappend(*fc_restrictive_policies, fc_policy);
		}
	}

	/*
	 * 我们按名称对严格策略进行排序，以便它们生成的任何 WCOs
	 * 以明确的顺序进行检查。
	 */
	sort_policies_by_name(*fc_restrictive_policies);

	/*
	 * 然后添加任何由扩展定义的宽松或严格政策。
	 * 如果适用于指定角色，则简单地附加到内部策略列表中。
	 */
	if (row_security_policy_hook_restrictive)
	{
		List	   *fc_hook_policies =
		(*row_security_policy_hook_restrictive) (fc_cmd, fc_relation);

		
/*
		 * 与内置限制性策略一样，我们也按名称对任何由钩子提供的
		 * 限制性策略进行排序。请注意，我们也故意始终检查所有内置限制性策略，
		 * 按名称顺序，在检查钩子添加的限制性策略之前，按名称顺序进行。
		 */
		sort_policies_by_name(fc_hook_policies);

		foreach(fc_item, fc_hook_policies)
		{
			RowSecurityPolicy *fc_policy = (RowSecurityPolicy *) lfirst(fc_item);

			if (fc_check_role_for_policy(fc_policy->roles, fc_user_id))
				*fc_restrictive_policies = lappend(*fc_restrictive_policies, fc_policy);
		}
	}

	if (row_security_policy_hook_permissive)
	{
		List	   *fc_hook_policies =
		(*row_security_policy_hook_permissive) (fc_cmd, fc_relation);

		foreach(fc_item, fc_hook_policies)
		{
			RowSecurityPolicy *fc_policy = (RowSecurityPolicy *) lfirst(fc_item);

			if (fc_check_role_for_policy(fc_policy->roles, fc_user_id))
				*fc_permissive_policies = lappend(*fc_permissive_policies, fc_policy);
		}
	}
}

/*
 * sort_policies_by_name
 *
 * 这仅用于限制性策略，确保它们生成的任何
 * WithCheckOptions 都以明确的顺序应用。
 * 这对于宽松政策不是必要的，因为它们都被使用 OR
 * 组合到一个单一的 WithCheckOption 检查中。
 */
static void sort_policies_by_name(List *fc_policies)
{
	list_sort(fc_policies, fc_row_security_policy_cmp);
}

/*
 * list_sort 比较器用于按名称对 RowSecurityPolicy 条目进行排序
 */
static int fc_row_security_policy_cmp(const ListCell *fc_a, const ListCell *fc_b)
{
	const RowSecurityPolicy *fc_pa = (const RowSecurityPolicy *) lfirst(fc_a);
	const RowSecurityPolicy *fc_pb = (const RowSecurityPolicy *) lfirst(fc_b);

	/* 防止扩展中的 NULL 政策名称 */
	if (fc_pa->policy_name == NULL)
		return fc_pb->policy_name == NULL ? 0 : 1;
	if (fc_pb->policy_name == NULL)
		return -1;

	return strcmp(fc_pa->policy_name, fc_pb->policy_name);
}

/*
 * add_security_quals
 *
 * 添加安全条件以强制执行指定的 RLS 策略，限制
 * 对表中现有数据的访问。如果没有策略控制
 * 对表的访问，则所有访问均被禁止 —— 即，使用
 * 隐式默认拒绝策略。
 *
 * 新的安全条件被添加到 securityQuals，如果添加的条件中包含
 * 子链接子查询，则将 hasSubLinks 设置为 true。
 */
static void add_security_quals(int fc_rt_index,
				   List *fc_permissive_policies,
				   List *fc_restrictive_policies,
				   List **fc_securityQuals,
				   bool *fc_hasSubLinks)
{
	ListCell   *fc_item;
	List	   *fc_permissive_quals = NIL;
	Expr	   *fc_rowsec_expr;

	/*
	 * 首先收集宽松的条件。如果我们没有找到任何
	 * 宽松政策，则没有行可见（这在下面处理）。
	 */
	foreach(fc_item, fc_permissive_policies)
	{
		RowSecurityPolicy *fc_policy = (RowSecurityPolicy *) lfirst(fc_item);

		if (fc_policy->qual != NULL)
		{
			fc_permissive_quals = lappend(fc_permissive_quals,
									   copyObject(fc_policy->qual));
			*fc_hasSubLinks |= fc_policy->hassublinks;
		}
	}

	/*
	 * 我们必须始终拥有宽松的条件，否则没有行可见。
	 *
	 * 如果没有，那么我们只需返回一个单一的 'false' 条件，这将导致
	 * 没有行可见。
	 */
	if (fc_permissive_quals != NIL)
	{
		/*
		 * 我们现在知道存在宽松政策，因此我们可以根据来自限制性
		 * 政策的 USING 子句添加安全条件。由于这些需要使用 AND
		 * 组合在一起，我们可以一次一个地添加它们。
		 */
		foreach(fc_item, fc_restrictive_policies)
		{
			RowSecurityPolicy *fc_policy = (RowSecurityPolicy *) lfirst(fc_item);
			Expr	   *fc_qual;

			if (fc_policy->qual != NULL)
			{
				fc_qual = copyObject(fc_policy->qual);
				ChangeVarNodes((Node *) fc_qual, 1, fc_rt_index, 0);

				*fc_securityQuals = list_append_unique(*fc_securityQuals, fc_qual);
				*fc_hasSubLinks |= fc_policy->hassublinks;
			}
		}

		/*
		 * 然后添加一个单一的安全条件，将所有允许策略中的 USING
		 * 子句组合在一起，使用 OR。
		 */
		if (list_length(fc_permissive_quals) == 1)
			fc_rowsec_expr = (Expr *) linitial(fc_permissive_quals);
		else
			fc_rowsec_expr = makeBoolExpr(OR_EXPR, fc_permissive_quals, -1);

		ChangeVarNodes((Node *) fc_rowsec_expr, 1, fc_rt_index, 0);
		*fc_securityQuals = list_append_unique(*fc_securityQuals, fc_rowsec_expr);
	}
	else

		/*
		 * 必须存在一个允许策略，才能使行可见。
		 * 因此，如果没有找到允许策略，则返回一个
		 * 始终为假的单一子句。
		 */
		*fc_securityQuals = lappend(*fc_securityQuals,
								 makeConst(BOOLOID, -1, InvalidOid,
										   sizeof(bool), BoolGetDatum(false),
										   false, true));
}

/*
 * add_with_check_options
 *
 * 添加指定类型的 WithCheckOptions，以检查通过 INSERT 或 UPDATE 添加的新记录
 * 是否与指定的 RLS 策略一致。通常，新数据必须满足政策中的 WITH CHECK 子句。
 * 如果一个策略没有明确的 WITH CHECK 子句，则使用其 USING 子句。
 * 在因 INSERT ... ON CONFLICT DO UPDATE 引发的 UPDATE 的特殊情况下，
 * 首先使用 WCO_RLS_CONFLICT_CHECK WithCheckOption 检查现有记录，
 * 该选项始终使用 RLS 策略中的 USING 子句。
 *
 * 新的 WCO 将被添加到 withCheckOptions 中，如果
 * 添加的任何检查子句包含子链接子查询，则 hasSubLinks 设置为 true。
 */
static void add_with_check_options(Relation fc_rel,
					   int fc_rt_index,
					   WCOKind fc_kind,
					   List *fc_permissive_policies,
					   List *fc_restrictive_policies,
					   List **fc_withCheckOptions,
					   bool *fc_hasSubLinks,
					   bool fc_force_using)
{
	ListCell   *fc_item;
	List	   *fc_permissive_quals = NIL;

#define QUAL_FOR_WCO(policy) \
	( !fc_force_using && \
	  (policy)->with_check_qual != NULL ? \
	  (policy)->with_check_qual : (policy)->qual )

	/*
	 * 首先收集允许策略子句，类似于
	 * add_security_quals。
	 */
	foreach(fc_item, fc_permissive_policies)
	{
		RowSecurityPolicy *fc_policy = (RowSecurityPolicy *) lfirst(fc_item);
		Expr	   *fc_qual = QUAL_FOR_WCO(fc_policy);

		if (fc_qual != NULL)
		{
			fc_permissive_quals = lappend(fc_permissive_quals, copyObject(fc_qual));
			*fc_hasSubLinks |= fc_policy->hassublinks;
		}
	}

	/*
	 * 必须找到至少一个允许条件，否则不允许添加任何行。
	 * 这与 add_security_quals 中的情况相同。
	 *
	 * 如果没有 permissive_quals，那么我们就会继续并返回一个
	 * 单一的 'false' WCO，阻止所有新行的添加。
	 */
	if (fc_permissive_quals != NIL)
	{
		/*
		 * 为所有允许策略子句添加一个单一的 WithCheckOption，
		 * 将它们组合在一起，使用 OR。此检查没有策略名称，
		 * 因为如果检查失败，这意味着没有策略授予
		 * 执行更新的权限，而不是任何特定政策被
		 * 违反。
		 */
		WithCheckOption *fc_wco;

		fc_wco = makeNode(WithCheckOption);
		fc_wco->kind = fc_kind;
		fc_wco->relname = pstrdup(RelationGetRelationName(fc_rel));
		fc_wco->polname = NULL;
		fc_wco->cascaded = false;

		if (list_length(fc_permissive_quals) == 1)
			fc_wco->qual = (Node *) linitial(fc_permissive_quals);
		else
			fc_wco->qual = (Node *) makeBoolExpr(OR_EXPR, fc_permissive_quals, -1);

		ChangeVarNodes(fc_wco->qual, 1, fc_rt_index, 0);

		*fc_withCheckOptions = list_append_unique(*fc_withCheckOptions, fc_wco);

		/*
		 * 现在为每个限制性政策子句添加 WithCheckOptions
		 * (它们将使用 AND 组合在一起)。我们为每个限制性政策使用一个单独的
		 * WithCheckOption，以便在策略被违反时可以将政策
		 * 名称包含在错误报告中。
		 */
		foreach(fc_item, fc_restrictive_policies)
		{
			RowSecurityPolicy *fc_policy = (RowSecurityPolicy *) lfirst(fc_item);
			Expr	   *fc_qual = QUAL_FOR_WCO(fc_policy);
			WithCheckOption *fc_wco;

			if (fc_qual != NULL)
			{
				fc_qual = copyObject(fc_qual);
				ChangeVarNodes((Node *) fc_qual, 1, fc_rt_index, 0);

				fc_wco = makeNode(WithCheckOption);
				fc_wco->kind = fc_kind;
				fc_wco->relname = pstrdup(RelationGetRelationName(fc_rel));
				fc_wco->polname = pstrdup(fc_policy->policy_name);
				fc_wco->qual = (Node *) fc_qual;
				fc_wco->cascaded = false;

				*fc_withCheckOptions = list_append_unique(*fc_withCheckOptions, fc_wco);
				*fc_hasSubLinks |= fc_policy->hassublinks;
			}
		}
	}
	else
	{
		/*
		 * 如果没有策略子句来检查新数据，则添加一个单一的
		 * 始终为假的 WCO（默认拒绝策略）。
		 */
		WithCheckOption *fc_wco;

		fc_wco = makeNode(WithCheckOption);
		fc_wco->kind = fc_kind;
		fc_wco->relname = pstrdup(RelationGetRelationName(fc_rel));
		fc_wco->polname = NULL;
		fc_wco->qual = (Node *) makeConst(BOOLOID, -1, InvalidOid,
									   sizeof(bool), BoolGetDatum(false),
									   false, true);
		fc_wco->cascaded = false;

		*fc_withCheckOptions = lappend(*fc_withCheckOptions, fc_wco);
	}
}

/*
 * check_role_for_policy -
 *	 确定当前角色是否应应用策略
 */
static bool fc_check_role_for_policy(ArrayType *fc_policy_roles, Oid fc_user_id)
{
	int			fc_i;
	Oid		   *fc_roles = (Oid *) ARR_DATA_PTR(fc_policy_roles);

	/* 快速通过适用于所有角色的策略 */
	if (fc_roles[0] == ACL_ID_PUBLIC)
		return true;

	for (fc_i = 0; fc_i < ARR_DIMS(fc_policy_roles)[0]; fc_i++)
	{
		if (has_privs_of_role(fc_user_id, fc_roles[fc_i]))
			return true;
	}

	return false;
}
